1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
use crate::algorithms::eval_dynamic::_attractors::saturated_reachability::{
reach_bwd, reachability_step,
};
use biodivine_lib_param_bn::biodivine_std::traits::Set;
use biodivine_lib_param_bn::symbolic_async_graph::{GraphColoredVertices, SymbolicAsyncGraph};
use biodivine_lib_param_bn::VariableId;
/// Uses a simplified Xie-Beerel algorithm adapted to coloured setting to find all bottom
/// SCCs in the given `universe` set. It only tests transitions using `active_variables`.
pub fn xie_beerel_attractors<F>(
graph: &SymbolicAsyncGraph,
universe: &GraphColoredVertices,
active_variables: &[VariableId],
mut on_component: F,
) where
F: FnMut(GraphColoredVertices) + Send + Sync,
{
let mut universe = universe.clone();
while !universe.is_empty() {
let pivots = universe.pick_vertex();
let pivot_basin = reach_bwd(graph, &pivots, &universe, active_variables);
let mut pivot_component = pivots.clone();
// Iteratively compute the pivot component. If some color leaves `pivot_basin`, it is
// removed from `pivot_component`, as it does not have to be processed any more.
//
// At the end of the loop, `pivot_component` contains only colors for which the component
// is an attractor (other colors will leave the `pivot_basin` at some point).
loop {
let done = reachability_step(
&mut pivot_component,
&universe,
active_variables,
|var, set| graph.var_post(var, set),
);
// This ensures `pivot_component` is still subset of `pivot_basin` even if we do not
// enforce it explicitly in `reachability_step`, since anything that leaks out
// is completely eliminated.
let escaped_basin = pivot_component.minus(&pivot_basin);
if !escaped_basin.is_empty() {
pivot_component = pivot_component.minus_colors(&escaped_basin.colors());
}
if done {
break;
}
}
if !pivot_component.is_empty() {
on_component(pivot_component);
}
universe = universe.minus(&pivot_basin);
}
}